package com.example.erat.service.impl;

import com.example.erat.dto.StatisticsDTO;
import com.example.erat.entity.ClassEntity;
import com.example.erat.entity.Course;
import com.example.erat.entity.Experiment;
import com.example.erat.entity.Student;
import com.example.erat.exception.BusinessException;
import com.example.erat.repository.ClassRepository;
import com.example.erat.repository.CourseRepository;
import com.example.erat.repository.ExperimentRepository;
import com.example.erat.repository.StudentRepository;
import com.example.erat.service.StatisticsService;
import com.example.erat.util.ExcelUtil;
import com.example.erat.util.FileUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 统计服务实现
 * 
 * 质量属性:
 * - 可扩展性(E2): 支持添加新的统计维度
 * - 可维护性(M1)S: 统计逻辑模块化，便于维护
 * - 性能(P1): 优化统计算法，提高性能
 */
@Slf4j
@Service
public class StatisticsServiceImpl implements StatisticsService {

    @Autowired
    private ClassRepository classRepository;
    
    @Autowired
    private CourseRepository courseRepository;
    
    @Autowired
    private ExperimentRepository experimentRepository;
    
    @Autowired
    private StudentRepository studentRepository;

    /**
     * 统计指定班级和课程的实验提交情况
     * 
     * 支持质量属性:
     * - 可用性(U2): 提供全面的统计功能
     * - 可扩展性(E2): 支持添加新的统计维度
     * - 性能(P1): 优化大量数据处理
     */
    @Override
    public StatisticsDTO statisticsByClassAndCourse(Long classId, Long courseId, String folderPath) {
        // 检查班级是否存在
        ClassEntity classEntity = classRepository.findById(classId)
                .orElseThrow(() -> new BusinessException("班级不存在"));
        
        // 检查课程是否存在
        Course course = courseRepository.findById(courseId)
                .orElseThrow(() -> new BusinessException("课程不存在"));
        
        // 获取该课程的所有实验
        List<Experiment> experiments = experimentRepository.findByCourseId(courseId);
        if (experiments.isEmpty()) {
            throw new BusinessException("该课程没有实验");
        }
        
        // 获取该班级的所有学生
        List<Student> students = studentRepository.findByClassEntityId(classId);
        if (students.isEmpty()) {
            throw new BusinessException("该班级没有学生");
        }
        
        // 创建组合统计器
        StatisticsComponent rootComponent = new CompositeStatisticsComponent();
        
        // 添加学生维度统计器
        rootComponent.add(new StudentStatisticsComponent(students, experiments, folderPath));
        
        // 添加实验维度统计器
        rootComponent.add(new ExperimentStatisticsComponent(students, experiments, folderPath));
        
        // 执行统计
        return rootComponent.statistics();
    }

    /**
     * 导出学生维度统计结果到Excel
     * 
     * 支持质量属性:
     * - 可用性(U1): 提供统计结果导出功能
     */
    @Override
    public byte[] exportStudentStatistics(Long classId, Long courseId, String folderPath) {
        StatisticsDTO statistics = statisticsByClassAndCourse(classId, courseId, folderPath);
        return ExcelUtil.exportStudentStatistics(statistics.getStudentStatistics());
    }

    /**
     * 导出实验维度统计结果到Excel
     * 
     * 支持质量属性:
     * - 可用性(U1): 提供统计结果导出功能
     */
    @Override
    public byte[] exportExperimentStatistics(Long classId, Long courseId, String folderPath) {
        StatisticsDTO statistics = statisticsByClassAndCourse(classId, courseId, folderPath);
        return ExcelUtil.exportExperimentStatistics(statistics.getExperimentStatistics());
    }
    
    /**
     * 统计组件接口 - 组合模式
     * 
     * 支持质量属性:
     * - 可扩展性(E2): 可以添加新的统计维度
     * - 可维护性(M1): 各统计组件职责单一
     */
    private interface StatisticsComponent {
        /**
         * 执行统计
         */
        StatisticsDTO statistics();
        
        /**
         * 添加子组件
         */
        void add(StatisticsComponent component);
        
        /**
         * 移除子组件
         */
        void remove(StatisticsComponent component);
        
        /**
         * 获取子组件
         */
        StatisticsComponent getChild(int index);
    }
    
    /**
     * 组合统计组件 - 组合模式
     * 
     * 支持质量属性:
     * - 可扩展性(E2): 可以动态组合多个统计组件
     * - 可维护性(M1): 统一管理多个统计组件
     */
    private static class CompositeStatisticsComponent implements StatisticsComponent {
        
        private final List<StatisticsComponent> children = new ArrayList<>();
        
        @Override
        public StatisticsDTO statistics() {
            StatisticsDTO result = new StatisticsDTO();
            
            for (StatisticsComponent component : children) {
                StatisticsDTO childResult = component.statistics();
                
                // 合并学生维度统计结果
                if (childResult.getStudentStatistics() != null && !childResult.getStudentStatistics().isEmpty()) {
                    result.setStudentStatistics(childResult.getStudentStatistics());
                }
                
                // 合并实验维度统计结果
                if (childResult.getExperimentStatistics() != null && !childResult.getExperimentStatistics().isEmpty()) {
                    result.setExperimentStatistics(childResult.getExperimentStatistics());
                }
            }
            
            return result;
        }
        
        @Override
        public void add(StatisticsComponent component) {
            children.add(component);
        }
        
        @Override
        public void remove(StatisticsComponent component) {
            children.remove(component);
        }
        
        @Override
        public StatisticsComponent getChild(int index) {
            return children.get(index);
        }
    }
    
    /**
     * 学生维度统计组件 - 组合模式叶子节点
     * 
     * 支持质量属性:
     * - 可维护性(M1): 职责单一，专注于学生维度统计
     * - 可靠性(R2): 处理文件夹不存在等异常情况
     */
    private static class StudentStatisticsComponent implements StatisticsComponent {
        
        private final List<Student> students;
        private final List<Experiment> experiments;
        private final String folderPath;
        
        public StudentStatisticsComponent(List<Student> students, List<Experiment> experiments, String folderPath) {
            this.students = students;
            this.experiments = experiments;
            this.folderPath = folderPath;
        }
        
        @Override
        public StatisticsDTO statistics() {
            StatisticsDTO result = new StatisticsDTO();
            List<StatisticsDTO.StudentStatistics> studentStatistics = new ArrayList<>();
            
            // 检查文件夹是否存在
            File folder = new File(folderPath);
            if (!folder.exists() || !folder.isDirectory()) {
                throw new BusinessException("实验文件夹不存在或不是文件夹: " + folderPath);
            }
            
            log.info("开始按学生维度统计，总学生数：{}，总实验数：{}", students.size(), experiments.size());
            
            // 创建实验文件夹路径映射，避免重复构建路径
            Map<String, String> experimentFolderPaths = new HashMap<>();
            for (Experiment experiment : experiments) {
                experimentFolderPaths.put(
                    experiment.getName(), 
                    folderPath + File.separator + experiment.getName()
                );
            }
            
            // 按照学生统计缺交情况
            for (Student student : students) {
                StatisticsDTO.StudentStatistics statistic = new StatisticsDTO.StudentStatistics();
                statistic.setStudentId(student.getStudentId());
                statistic.setName(student.getName());
                
                List<String> missingExperiments = new ArrayList<>();
                
                // 为提高效率，为每个学生创建只包含该学生的列表
                List<Student> singleStudentList = Collections.singletonList(student);
                
                // 检查每个实验的提交情况
                for (Experiment experiment : experiments) {
                    String experimentFolderPath = experimentFolderPaths.get(experiment.getName());
                    File expFolder = new File(experimentFolderPath);
                    
                    // 如果实验文件夹不存在，认为所有学生都未提交
                    if (!expFolder.exists() || !expFolder.isDirectory()) {
                        log.warn("实验文件夹不存在: {}", experimentFolderPath);
                        missingExperiments.add(experiment.getName());
                        continue;
                    }
                    
                    // 扫描实验文件夹
                    Map<String, List<Student>> scanResult = FileUtil.scanExperimentFolder(
                            experimentFolderPath, experiment, singleStudentList);
                    
                    // 如果学生在未提交列表中，则添加到缺交列表
                    if (!scanResult.get("unsubmitted").isEmpty()) {
                        missingExperiments.add(experiment.getName());
                    }
                }
                
                statistic.setMissingExperiments(missingExperiments);
                statistic.setMissingCount(missingExperiments.size());
                
                studentStatistics.add(statistic);
            }
            
            log.info("学生维度统计完成，结果数量：{}", studentStatistics.size());
            result.setStudentStatistics(studentStatistics);
            return result;
        }
        
        @Override
        public void add(StatisticsComponent component) {
            throw new UnsupportedOperationException("叶子节点不支持添加子组件");
        }
        
        @Override
        public void remove(StatisticsComponent component) {
            throw new UnsupportedOperationException("叶子节点不支持移除子组件");
        }
        
        @Override
        public StatisticsComponent getChild(int index) {
            throw new UnsupportedOperationException("叶子节点不支持获取子组件");
        }
    }
    
    /**
     * 实验维度统计组件 - 组合模式叶子节点
     * 
     * 支持质量属性:
     * - 可维护性(M1): 职责单一，专注于实验维度统计
     * - 可靠性(R2): 处理文件夹不存在等异常情况
     * - 性能(P1): 优化统计算法
     */
    private static class ExperimentStatisticsComponent implements StatisticsComponent {
        
        private final List<Student> students;
        private final List<Experiment> experiments;
        private final String folderPath;
        
        public ExperimentStatisticsComponent(List<Student> students, List<Experiment> experiments, String folderPath) {
            this.students = students;
            this.experiments = experiments;
            this.folderPath = folderPath;
        }
        
        @Override
        public StatisticsDTO statistics() {
            StatisticsDTO result = new StatisticsDTO();
            List<StatisticsDTO.ExperimentStatistics> experimentStatistics = new ArrayList<>();
            
            // 检查文件夹是否存在
            File folder = new File(folderPath);
            if (!folder.exists() || !folder.isDirectory()) {
                throw new BusinessException("实验文件夹不存在或不是文件夹: " + folderPath);
            }
            
            log.info("开始按实验维度统计，总实验数：{}，总学生数：{}", experiments.size(), students.size());
            
            // 按照实验统计缺交情况
            for (Experiment experiment : experiments) {
                StatisticsDTO.ExperimentStatistics statistic = new StatisticsDTO.ExperimentStatistics();
                statistic.setExperimentName(experiment.getName());
                
                String experimentFolderPath = folderPath + File.separator + experiment.getName();
                File expFolder = new File(experimentFolderPath);
                
                // 如果实验文件夹不存在，认为所有学生都未提交
                List<String> missingStudents;
                int missingCount;
                
                if (!expFolder.exists() || !expFolder.isDirectory()) {
                    log.warn("实验文件夹不存在: {}", experimentFolderPath);
                    // 所有学生都未提交
                    missingStudents = students.stream()
                            .map(s -> s.getName() + "(" + s.getStudentId() + ")")
                            .collect(Collectors.toList());
                    missingCount = students.size();
                } else {
                    // 扫描实验文件夹
                    Map<String, List<Student>> scanResult = FileUtil.scanExperimentFolder(
                            experimentFolderPath, experiment, students);
                    
                    List<Student> unsubmittedStudents = scanResult.get("unsubmitted");
                    missingStudents = unsubmittedStudents.stream()
                            .map(s -> s.getName() + "(" + s.getStudentId() + ")")
                            .collect(Collectors.toList());
                    
                    missingCount = missingStudents.size();
                }
                
                statistic.setMissingStudents(missingStudents);
                statistic.setMissingCount(missingCount);
                
                // 计算提交率
                int totalStudents = students.size();
                int submittedCount = totalStudents - missingCount;
                double submissionRate = totalStudents > 0 ? (double) submittedCount / totalStudents : 0;
                statistic.setSubmissionRate(submissionRate);
                
                experimentStatistics.add(statistic);
                log.info("实验[{}]统计完成，提交率：{}", experiment.getName(), submissionRate);
            }
            
            log.info("实验维度统计完成，结果数量：{}", experimentStatistics.size());
            result.setExperimentStatistics(experimentStatistics);
            return result;
        }
        
        @Override
        public void add(StatisticsComponent component) {
            throw new UnsupportedOperationException("叶子节点不支持添加子组件");
        }
        
        @Override
        public void remove(StatisticsComponent component) {
            throw new UnsupportedOperationException("叶子节点不支持移除子组件");
        }
        
        @Override
        public StatisticsComponent getChild(int index) {
            throw new UnsupportedOperationException("叶子节点不支持获取子组件");
        }
    }
} 